package resume_service

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"peilian-api/app/global/common"
	"peilian-api/app/global/databases/tables"
	"peilian-api/app/global/errors"
	"peilian-api/app/global/log"
	"peilian-api/app/global/variable"
	"peilian-api/app/model"
	"peilian-api/utils/function"
	"peilian-api/utils/paginate"
	"peilian-api/utils/sdk"
	"peilian-api/utils/tools"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis"
)

type ResumeService struct{}

func (rs *ResumeService) GetOptResumeList(c *gin.Context, gorl model.GetOptResumeListReq) (int64, []*tables.GetOptResumeListResp, *errors.ErrRes) {
	all := make([]*tables.GetOptResumeListResp, 0)
	uid, exists := c.Get("uid")
	id, _ := uid.(uint)
	if !exists {
		return 0, all, errors.NewErrInfo(10001, fmt.Errorf("请以用户身份访问"))
	}
	gorl.UserId = id
	allQuery := variable.DB.MySQL.Model(&tables.ResumeRecord{}).Where("user_id=?", gorl.UserId)
	var count int64
	if err := allQuery.Count(&count).Error; err != nil {
		return 0, all, errors.NewErrInfo(10002, err)
	}
	if err := allQuery.Order("is_viewed,created_at desc").Scopes(paginate.LowerPaginate(c.Request)).Scan(&all).Error; err != nil {
		return 0, all, errors.NewErrInfo(10002, err)
	}
	for i := range all {
		all[i].UpdTimeStr = all[i].UpdatedAt.In(time.FixedZone("CST", 8*3600)).Format("2006-01-02 15:04:05")
		all[i].CreateTimeStr = all[i].CreatedAt.In(time.FixedZone("CST", 8*3600)).Format("2006-01-02 15:04:05")
	}
	return count, all, nil
}
func (rs *ResumeService) GetOptResumeItem(c *gin.Context, gorl model.GetOptResumeItemReq) (tables.ResumeRecord, *errors.ErrRes) {
	var resume_record tables.ResumeRecord
	// 获取该用户的id
	uid, exists := c.Get("uid")
	if !exists {
		return resume_record, errors.NewErrInfo(10001, fmt.Errorf("请以用户身份访问"))
	}
	check_res := rs.CheckIsResumeOwner(uid.(uint), gorl.Id)
	if check_res == 0 {
		return resume_record, errors.NewErrInfo(10002, fmt.Errorf("该项简历优化记录不存在"))
	} else if check_res == 1 {
		return resume_record, errors.NewErrInfo(10003, fmt.Errorf("不能获取非您创建的简历优化记录"))
	}
	variable.DB.MySQL.Model(&tables.ResumeRecord{}).Where("id=?", gorl.Id).Update("is_viewed", 1)

	// 查看了简历，我们需要更新查看状态到redis, 简历已经被查看，从队列中删除其id
	variable.DB.Redis.ZRem(fmt.Sprintf(common.RedisResumeIsViewedKey, uid), gorl.Id)

	res := variable.DB.MySQL.Model(&tables.ResumeRecord{}).Where("id=?", gorl.Id).Scan(&resume_record)
	err, r := res.Error, res.RowsAffected
	if err != nil || r == 0 {
		if err != nil {
			return resume_record, errors.NewErrInfo(10001, err)
		} else {
			return resume_record, errors.NewErrInfo(10002, fmt.Errorf("未找到该记录"))
		}

	}
	return resume_record, nil
}

func (rs *ResumeService) UpdateOptResumeItem(c *gin.Context, uori model.UpdateOptResumeItemReq) *errors.ErrRes {
	// 1. 获取该用户的id
	_, exists := c.Get("uid")
	if !exists {
		return errors.NewErrInfo(10001, fmt.Errorf("请以用户身份访问"))
	}
	// 2.检查
	id := c.Param("id")
	if len(id) == 0 {
		return errors.NewErrInfo(10002, fmt.Errorf("Resume id不能为空"))
	}
	rid, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return errors.NewErrInfo(10003, fmt.Errorf("Resume id必须为数字"))
	}
	uori.Id = uint(rid)
	if uori.Name == "" || tools.HanZiLen(uori.Name) > 12 {
		return errors.NewErrInfo(10004, fmt.Errorf("优化简历名称不能为空且长度不能超过12个汉字"))
	}
	// 3. 更新
	result := variable.DB.MySQL.Model(&tables.ResumeRecord{}).Where("id=?", uori.Id).Update("name", uori.Name)
	if result.Error != nil {
		return errors.NewErrInfo(10005, result.Error)
	}
	if result.RowsAffected <= 0 {
		return errors.NewErrInfo(10006, fmt.Errorf("未找到记录!"))
	}

	return nil
}

func (rs *ResumeService) GenerateOpt(c *gin.Context, gor model.GenerateOptReq) *errors.ErrRes {
	// 1. 获取该用户的id
	uid, exists := c.Get("uid")
	if !exists {
		return errors.NewErrInfo(30001, fmt.Errorf("请以用户身份访问"))
	}

	// 2.检查
	exp, jd, id := gor.Exp, gor.Jd, gor.Id
	userId, _ := uid.(uint)
	if len(exp) == 0 {
		return errors.NewErrInfo(30001, fmt.Errorf("经历实践不能为空"))
	}
	if tools.HanZiLen(exp) > 500 || tools.HanZiLen(jd) > 500 {
		return errors.NewErrInfo(30001, fmt.Errorf("经历实践和职位描述长度不能超过500个汉字"))
	}

	// 3. 判断是否重新生成 id = 0 初次生成 & id = 1 重新生成
	var resume_record tables.ResumeRecord
	if id != 0 {
		d := variable.DB.MySQL.Where("id = ?", gor.Id).Find(&resume_record)
		if d.RowsAffected == 0 {
			return errors.NewErrInfo(30002, fmt.Errorf("重新生成失败，未找到该记录"))
		}
		if resume_record.User_id != uid { // 非本人创建的简历优化记录不能重新生成
			return errors.NewErrInfo(30003, fmt.Errorf("不能重新生成非您创建的简历优化记录！"))
		}
		if resume_record.ResumeStatus == 1 { // 简历优化记录生成中，不能重新生成
			return errors.NewErrInfo(30004, fmt.Errorf("简历生成中，生成完成后再操作~"))
		}
		// 删除原来的
		err := new(tables.ResumeRecord).DeleteResumeById(int(id))
		if err != nil {
			return errors.NewErrInfo(30005, fmt.Errorf("操作失败，请重新操作~"))
		}
		variable.DB.Redis.ZRem(fmt.Sprintf(common.RedisResumeIsGenerateKey, userId), resume_record.ID) // 删除原来的数据

		// 初始化数据
		resume_record.ID = 0
		resume_record.ResumeStatus = 1 // 重新生成
		resume_record.IsViewed = 0
		resume_record.Opt_exp = ""
		if err := variable.DB.MySQL.Create(&resume_record).Error; err != nil {
			return errors.NewErrInfo(30001, fmt.Errorf("操作失败, 请重试~"))
		}
	} else {
		resume_record.Name = "优化结果"
		resume_record.User_id = userId
		resume_record.ResumeStatus = 1 // 设置为生成中
		resume_record.Jd = jd
		resume_record.Exp = exp
		// 创建
		if err := variable.DB.MySQL.Create(&resume_record).Error; err != nil {
			return errors.NewErrInfo(30001, fmt.Errorf("操作失败, 请重试~"))
		}
	}

	// 4. 添加简历生成中
	ic := variable.DB.Redis.ZAdd(fmt.Sprintf(common.RedisResumeIsGenerateKey, userId), redis.Z{Member: resume_record.ID, Score: 0})
	if ic.Err() != nil {
		log.Logger.ErrorMsgF(c, fmt.Sprintf("resume doing status add error, key:%s, id: %d", fmt.Sprintf(common.RedisResumeIsGenerateKey, userId), resume_record.ID))
	}
	var queryType = common.GlmYsQueryTypeWithJd
	jdStr := strings.Trim(resume_record.Jd, " ")
	if len(jdStr) == 0 {
		queryType = common.GlmYsQueryTypeWithoutJd
	}
	// 5. 发送消息 封装消息
	rm := &model.ResumeMsg{
		AppId:     common.GlmYsAppid,
		QueryType: queryType,
		UserId:    fmt.Sprintf("%d_%d", userId, resume_record.ID),
		Rc: &model.ResumeContent{
			Resume: resume_record.Exp,
			Jd:     resume_record.Jd,
		},
	}

	b, _ := json.Marshal(rm)
	go rs.AskGlm(c, string(b))
	//_, err := new(yf_service.YfApi).SendMsg(c, variable.Config.Yf.TopicYoushi, common.GenerateOptType, string(b))
	//if err != nil {
	//	log.Logger.ErrorMsgF(c, fmt.Sprintf("send msg error, %s, error: %s", string(b), err))
	//}
	//
	return nil
}

// AskGlm 直接访问大模型接口
func (rs *ResumeService) AskGlm(ctx context.Context, message string) {
	body := map[string]string{
		"message": message,
	}
	sendBody, _ := json.Marshal(body)
	// 发送http 请求
	resp, err := http.Post(variable.Config.Services.AskGlm, "application/json", bytes.NewReader(sendBody))
	if err != nil {
		log.Logger.ErrorF(ctx, "接口调用失败: "+err.Error())
		return
	}
	if resp != nil {
		defer resp.Body.Close()
	}
	respInfo, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Logger.ErrorF(ctx, "结果解析失败: "+err.Error())
		return
	}
	log.Logger.InfoF(ctx, "接口调用完成 result: "+string(respInfo))
}

func (rs *ResumeService) ResendResumeCancel(c *gin.Context, jd string, user_id uint, resume_id uint, exp string) {
	var queryType = common.GlmYsQueryTypeWithJd
	jdStr := strings.Trim(jd, " ")
	if len(jdStr) == 0 {
		queryType = common.GlmYsQueryTypeWithoutJd
	}
	// 5. 发送消息 封装消息
	rm := &model.ResumeMsg{
		AppId:     common.GlmYsAppid,
		QueryType: queryType,
		UserId:    fmt.Sprintf("%d_%d", user_id, resume_id),
		Rc: &model.ResumeContent{
			Resume: exp,
			Jd:     jd,
		},
	}

	b, _ := json.Marshal(rm)
	go rs.AskGlm(c, string(b))
	//new(yf_service.YfApi).SendMsg(c, variable.Config.Yf.TopicYoushi, common.GenerateOptType, string(b))
}

// 不使用消息，轮询的方式去查询
func (rs *ResumeService) GenerationResumeFinish(c *gin.Context) error {
	// 1. 查询所有的生成中的简历
	var records []tables.ResumeRecord
	d := variable.DB.MySQL.Model(tables.ResumeRecord{}).Where("resume_status != ?", 2).Find(&records)
	if d.Error != nil {
		return nil
	}
	if len(records) == 0 {
		return nil
	}
	log.Logger.InfoMsgF(c, fmt.Sprintf("GenerationResumeFinish records: %d", len(records)))

	currentTime := time.Now().Unix()
	for _, v := range records {
		t := currentTime - v.CreatedAt.Unix()/60*60
		if t%300 == 0 { // 防止 finishAt 是 0000-00-00 00:00:00.000  -- 每5分钟报警一次
			log.Logger.InfoMsgF(c, fmt.Sprintf("简历优化超过5分钟未生成结果:%d", v.ID))
			// 失败了，重新生成 报警
			rs.ResendResumeCancel(c, v.Jd, v.User_id, v.ID, v.Exp)
		}
		// 2. 调用接口请求get
		var queryType = common.GlmYsQueryTypeWithJd
		jds := strings.Trim(v.Jd, " ")
		if len(jds) == 0 {
			queryType = common.GlmYsQueryTypeWithoutJd
		}
		grt, err := GetGlmResult(c, v.User_id, v.ID, queryType)
		var optExp = ""
		if err != nil {
			rs.ResendResumeCancel(c, v.Jd, v.User_id, v.ID, v.Exp)
			continue
		}

		if strings.Contains(grt.Status, "not_complete") {
			continue
		} else if grt.Status == "success" {
			tmp_text := grt.ResText
			optExp = function.FilterNumberAndMask(tmp_text)
		} else {
			log.Logger.InfoMsgF(c, fmt.Sprintf("gml result status: %s, id: %d", grt.Status, v.ID))
			continue
		}
		// 3. 接口调用成功，将结果写入数据库中
		err = new(tables.ResumeRecord).UpdateResumeById(map[string]interface{}{"opt_exp": optExp, "resume_status": 2}, int(v.ID))
		if err != nil {
			log.Logger.InfoMsgF(c, fmt.Sprintf("gml result data update db error, id: %d, opt_exp: %s", v.ID, optExp))
			continue
		}

		// 4. 删除redis 状态已经变成redis
		ic := variable.DB.Redis.ZRem(fmt.Sprintf(common.RedisResumeIsGenerateKey, v.User_id), v.ID)
		if ic.Err() != nil {
			log.Logger.InfoMsgF(c, fmt.Sprintf("简历优化redis 状态删除失败:%d, key: %s", v.ID, fmt.Sprintf(common.RedisResumeIsGenerateKey, v.User_id)))
		}

		// 5. 设置为未查看状态
		ic = variable.DB.Redis.ZAdd(fmt.Sprintf(common.RedisResumeIsViewedKey, v.User_id), redis.Z{Member: v.ID, Score: 0})
		if ic.Err() != nil {
			log.Logger.InfoMsgF(c, fmt.Sprintf("简历优化redis 状态设置未查看失败:%d, key: %s", v.ID, fmt.Sprintf(common.RedisResumeIsViewedKey, v.User_id)))
		}
	}

	return nil
}

func (rs *ResumeService) CheckIsResumeOwner(user_id, resume_id uint) int {
	var resume_record tables.ResumeRecord
	d := variable.DB.MySQL.Where("id = ?", resume_id).Find(&resume_record)
	if d.RowsAffected == 0 {
		return 0 //没查找到这条记录
	}
	if user_id != resume_record.User_id {
		return 1 //不是本人创建的简历优化记录
	}
	return 2 //是本人创建的简历优化记录
}

func (rs *ResumeService) GetOptResumeIsViewedById(c *gin.Context, resume_id int) (uint, *errors.ErrRes) {
	redisValue, err := variable.DB.Redis.Get(fmt.Sprintf(common.RedisResumeIsViewedKey, resume_id)).Result()
	if err != nil {
		var resume_record tables.ResumeRecord
		res := variable.DB.MySQL.Model(&tables.ResumeRecord{}).Where("id=?", resume_id).Scan(&resume_record)
		err, r := res.Error, res.RowsAffected
		if err != nil || r == 0 {
			if err != nil {
				return 1, errors.NewErrInfo(10001, err)
			} else {
				return 1, errors.NewErrInfo(10002, fmt.Errorf("未找到该记录"))
			}
		}
		return resume_record.IsViewed, nil
	}
	status, _ := strconv.ParseInt(redisValue, 10, 64)
	return uint(status), nil
}

func (rs *ResumeService) CheckIsAllViewed(c *gin.Context, uid uint) (int, *errors.ErrRes) {
	// allQuery := variable.DB.MySQL.Model(&tables.ResumeRecord{}).Where("user_id=?", uid)
	// var count int64
	// if err := allQuery.Count(&count).Error; err != nil {
	// 	return 0, errors.NewErrInfo(10001, err)
	// }
	// viewedQuery := variable.DB.MySQL.Model(&tables.ResumeRecord{}).Where("user_id=? and is_viewed=?", uid, 1)
	// var viewedCount int64
	// if err := viewedQuery.Count(&viewedCount).Error; err != nil {
	// 	return 0, errors.NewErrInfo(10001, err)
	// }
	// if viewedCount == count {
	// 	return 1, nil
	// } else {
	// 	return 0, nil
	// }

	ssc := variable.DB.Redis.ZRange(fmt.Sprintf(common.RedisResumeIsViewedKey, uid), 0, -1)
	if ssc.Err() != nil {
		return 0, nil
	}
	s, _ := ssc.Result()
	return len(s), nil

}

// 同步redis与mysql数据 主要校对两个字段
// 1.peilian_resume_is_viewed_user_XXX
// 2.peilian_resume_is_generate_user_XXX
func (rs *ResumeService) JudgeResume(ctx *gin.Context) {
	sendmsg := ""
	// 检测简历大师中 用户是否全部已阅
	ReSumes, _ := new(tables.ResumeRecord).GetResumesByWhere(map[string]interface{}{}, "")
	for _, r := range ReSumes {
		rid, uid := r.ID, r.User_id

		// 1. 检测 peilian_resume_is_viewed_user_XXX
		// 1.1 获取redis中的数据
		_, err := variable.DB.Redis.ZRange(fmt.Sprintf(common.RedisResumeIsViewedKey, uid), 0, -1).Result()
		if err != nil {
			log.Logger.InfoMsgF(ctx, fmt.Sprintf("JudgeResume 获取redis中的peilian_resume_is_viewed_user数据失败:%s", err.Error()))
			continue
		}
		res := rs.CheckIsInRedis(rid, uid, common.RedisResumeIsViewedKey)
		if res == strconv.Itoa(int(rid)) { //redis里面有以peilian_resume_is_viewed_user_XXX为键，以简历id为值的这条记录
			if r.IsViewed == 1 { //用户有查看过这条，redis记录删除
				variable.DB.Redis.ZRem(fmt.Sprintf(common.RedisResumeIsViewedKey, uid), r.ID)
				sendmsg += fmt.Sprintf("用户:%d,简历:%d,状态为已阅,redis未读记录删除成功\n", uid, r.ID)
			}
		} else {
			if r.IsViewed == 0 { //用户没查看过，redis却没有，需要添加
				variable.DB.Redis.ZAdd(fmt.Sprintf(common.RedisResumeIsViewedKey, uid), redis.Z{Member: r.ID, Score: 0})
				sendmsg += fmt.Sprintf("用户:%d,简历:%d,状态为未阅,redis中没有未读数据,修正成功\n", uid, r.ID)
			}
		}

		// 2 检测 peilian_resume_is_generate_user_XXX
		// 2.1 获取redis中的数据
		_, err = variable.DB.Redis.ZRange(fmt.Sprintf(common.RedisResumeIsViewedKey, uid), 0, -1).Result()
		if err != nil {
			log.Logger.InfoMsgF(ctx, fmt.Sprintf("JudgeResume 获取redis中的peilian_resume_is_viewed_user数据失败:%s", err.Error()))
			continue
		}
		res = rs.CheckIsInRedis(rid, uid, common.RedisResumeIsGenerateKey)
		if res == strconv.Itoa(int(rid)) { //redis里面有以peilian_resume_is_generate_user_XXX为键，以简历id为值的记录
			if r.ResumeStatus == 2 { //这条记录已经生成，redis删除
				variable.DB.Redis.ZRem(fmt.Sprintf(common.RedisResumeIsViewedKey, uid), r.ID)
				sendmsg += fmt.Sprintf("用户:%d,简历:%d,状态为已生成,redis未生成记录删除成功\n", uid, r.ID)
			}
		} else {
			if r.ResumeStatus != 2 { //这条记录未生成，redis添加
				variable.DB.Redis.ZAdd(fmt.Sprintf(common.RedisResumeIsViewedKey, uid), redis.Z{Member: r.ID, Score: 0})
				sendmsg += fmt.Sprintf("用户:%d,简历:%d,状态为未生成,redis中没有未生成数据,添加成功\n", uid, r.ID)
			}
		}

	}
	if sendmsg != "" {
		sdk.SendMsg(sendmsg)
	}
}

// 判断redis里面是否存在如下两种数据
// 以peilian_resume_is_viewed_user_XXX为键，以简历id为值的数据
// 以peilian_resume_is_generate_user_XXX为键，以简历id为值的数据
func (rs *ResumeService) CheckIsInRedis(Rid, Uid uint, Query string) string {
	q_str := fmt.Sprintf(Query, Uid)
	rList, err := variable.DB.Redis.ZRange(q_str, 0, -1).Result()
	if err != nil {
		return ""
	}
	if len(rList) == 0 {
		return ""
	}
	for _, v := range rList {
		if v == strconv.Itoa(int(Rid)) {
			return v
		}
	}
	return ""
}
